package com.zhangdi.mserver.searchcli.utils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author zhangdi
 * @version 1.0
 * @date 2022.2.12 9:20
 */
public class BeanUtils {
    public static <T> T mapToBean(Class<T> type, Map<String, Object> map) {
        T obj = null;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(type);
            obj = type.newInstance();
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor descriptor : propertyDescriptors) {
                String propertyName = descriptor.getName();
                if (map.containsKey(propertyName)) {
                    Object value = map.get(propertyName);
                    if (value instanceof Collection<?>) {
                        Collection<Object> objectCollection = new ArrayList<>();
                        Iterator<?> iterator = ((Collection<?>) value).iterator();
                        while (iterator.hasNext()) {
                            Object item = iterator.next();
                            if (item instanceof HashMap) {
                                Field field = findField(type, propertyName);
                                if (field != null) {
                                    Type genericType = field.getGenericType();
                                    if (genericType instanceof ParameterizedType) {
                                        ParameterizedType pt = (ParameterizedType) genericType;
                                        Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
                                        Object genericObj = actualTypeArgument.newInstance();
                                        Field[] genericObjFields = actualTypeArgument.getDeclaredFields();
                                        Map<Object, Object> elMap = (HashMap<Object, Object>) item;
                                        for (Field genericObjField : genericObjFields) {
                                            genericObjField.setAccessible(true);
                                            if (elMap.containsKey(genericObjField.getName())) {
                                                genericObjField.set(genericObj, elMap.get(genericObjField.getName()));
                                            }
                                        }
                                        objectCollection.add(genericObj);
                                    }
                                } else {
                                    objectCollection.add(item);
                                }
                            } else {
                                objectCollection.add(item);
                            }
                        }
                        descriptor.getWriteMethod().invoke(obj, objectCollection);
                    } else {
                        descriptor.getWriteMethod().invoke(obj, value);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    private static Field findField(Class<?> clazz, String propertyName) {
        if (clazz == null) {
            return null;
        }
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().equals(propertyName)) {
                return field;
            }
        }

        return findField(clazz.getSuperclass(), propertyName);
    }
}
